#if defined(_MSC_VER)
#pragma once
#endif

#ifndef LM_CORE_VECTOR4_H
#define LM_CORE_VECTOR4_H

#include "lm.h"

class Vector4
{
public:
	Vector4() : x(0), y(0), z(0), w(0) { }
	Vector4(float val) : x(val), y(val), z(val), w(val) { }
	Vector4(float vx, float vy, float vz, float vw) : x(vx), y(vy), z(vz), w(vw) {}
	explicit Vector4(const Vector3 &v);
	explicit Vector4(const Normal &n);
	explicit Vector4(const Point3 &p);
#ifndef NDEBUG
	Vector4(const Vector4 &v) : x(v.x), y(v.y), z(v.z), w(v.w) {
		Assert(!HasNaNs());
	}
	Vector4 &operator=(const Vector4 &v) {
		x = v.x; y = v.y; z = v.z; w = v.w;
		Assert(!HasNaNs());
		return *this;
	}
#endif
	Vector4 operator+(const Vector4 &v) const {
		Assert(!HasNaNs());
		return Vector4(x+v.x, y+v.y, z+v.z, w+v.w);
	}
	Vector4 &operator+=(const Vector4 &v) {
		Assert(!HasNaNs());
		x += v.x; y += v.y; z += v.z; w += v.w;
		return *this;
	}
	Vector4 operator-(const Vector4 &v) const {
		Assert(!HasNaNs());
		return Vector4(x-v.x, y-v.y, z-v.z, w-v.w);
	}
	Vector4 &operator-=(const Vector4 &v) {
		Assert(!HasNaNs());
		x -= v.x; y -= v.y; z -= v.z; w -= v.w;
		return *this;
	}
	Vector4 operator*(float f) const {
		Assert(!HasNaNs());
		return Vector4(x*f, y*f, z*f, w*f);
	}
	Vector4 &operator*=(float f) {
		Assert(!HasNaNs());
		x *= f; y *= f; z *= f; w *= f;
		return *this;
	}
	Vector4 operator/(float f) const {
		Assert(!HasNaNs());
		if(f == 0)
			Warning("Division by 0 in file %s, line %d\n",__FILE__,__LINE__);
		float invf = (float)1 / f;
		return Vector4(x*invf, y*invf, z*invf, w*invf);
	}
	Vector4 &operator/=(float f) {
		Assert(!HasNaNs());
		if(f == 0)
			Warning("Division by 0 in file %s, line %d\n",__FILE__,__LINE__);
		float invf = (float)1 / f;
		x *= invf; y *= invf; z *= invf; w *= invf;
		return *this;
	}
	Vector4 operator-() const {
		return Vector4(-x, -y, -z, -w);
	}
	bool operator==(const Vector4 &v) const {
		return x == v.x && y == v.y && z == v.z && w == v.w;
	}
	bool operator!=(const Vector4 &v) const {
		return !(*this == v);
	}
	float operator[](int i) const {
		Assert(i >= 0 && i <= 3);
		return (&x)[i];
	}
	float &operator[](int i) {
		Assert(i >=0 && i <= 3);
		return (&x)[i];
	}
	float LengthSqured() const {
		return x*x + y*y + z*z + w*w;
	}
	float Length() const {
		return sqrtf(x*x + y*y + z*z + w*w);
	}
	bool IsZero() const {
		return x == 0 && y == 0 && z == 0 && w == 0;
	}
	bool HasNaNs() const {
		return isnan(x) || isnan(y) || isnan(z) || isnan(w);
	}
	// Return a readable string representation of this vector
	std::string ToString() const {
		std::ostringstream oss;
		oss << "Vector4 " << "[" << x << ", " << y << ", "<< z <<", "<< w << "]";
		return oss.str();
	}
	float x, y, z, w;
};

#endif	// LM_CORE_VECTOR4_H